home *** CD-ROM | disk | FTP | other *** search
- /* uniq -- remove duplicate lines from a sorted file
- Copyright (C) 86, 91, 1995-1998, 1999 Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software Foundation,
- Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
-
- /* Written by Richard Stallman and David MacKenzie. */
- /* 2000-03-22 Trimmed down to the case of "uniq -u" by Bruno Haible. */
-
- #include <stddef.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
-
- /* The name this program was run with. */
- static char *program_name;
-
- static void
- xalloc_fail (void)
- {
- fprintf (stderr, "%s: virtual memory exhausted\n", program_name);
- exit (1);
- }
-
- /* Allocate N bytes of memory dynamically, with error checking. */
-
- void *
- xmalloc (size_t n)
- {
- void *p;
-
- p = malloc (n);
- if (p == 0)
- xalloc_fail ();
- return p;
- }
-
- /* Change the size of an allocated block of memory P to N bytes,
- with error checking.
- If P is NULL, run xmalloc. */
-
- void *
- xrealloc (void *p, size_t n)
- {
- p = realloc (p, n);
- if (p == 0)
- xalloc_fail ();
- return p;
- }
-
- /* A `struct linebuffer' holds a line of text. */
-
- struct linebuffer
- {
- size_t size; /* Allocated. */
- size_t length; /* Used. */
- char *buffer;
- };
-
- /* Initialize linebuffer LINEBUFFER for use. */
-
- static void
- initbuffer (struct linebuffer *linebuffer)
- {
- linebuffer->length = 0;
- linebuffer->size = 200;
- linebuffer->buffer = (char *) xmalloc (linebuffer->size);
- }
-
- /* Read an arbitrarily long line of text from STREAM into LINEBUFFER.
- Keep the newline; append a newline if it's the last line of a file
- that ends in a non-newline character. Do not null terminate.
- Return LINEBUFFER, except at end of file return 0. */
-
- static struct linebuffer *
- readline (struct linebuffer *linebuffer, FILE *stream)
- {
- int c;
- char *buffer = linebuffer->buffer;
- char *p = linebuffer->buffer;
- char *end = buffer + linebuffer->size - 1; /* Sentinel. */
-
- if (feof (stream) || ferror (stream))
- return 0;
-
- do
- {
- c = getc (stream);
- if (c == EOF)
- {
- if (p == buffer)
- return 0;
- if (p[-1] == '\n')
- break;
- c = '\n';
- }
- if (p == end)
- {
- linebuffer->size *= 2;
- buffer = (char *) xrealloc (buffer, linebuffer->size);
- p = p - linebuffer->buffer + buffer;
- linebuffer->buffer = buffer;
- end = buffer + linebuffer->size - 1;
- }
- *p++ = c;
- }
- while (c != '\n');
-
- linebuffer->length = p - buffer;
- return linebuffer;
- }
-
- /* Free linebuffer LINEBUFFER's data. */
-
- static void
- freebuffer (struct linebuffer *linebuffer)
- {
- free (linebuffer->buffer);
- }
-
- /* Undefine, to avoid warning about redefinition on some systems. */
- #undef min
- #define min(x, y) ((x) < (y) ? (x) : (y))
-
- /* Return zero if two strings OLD and NEW match, nonzero if not.
- OLD and NEW point not to the beginnings of the lines
- but rather to the beginnings of the fields to compare.
- OLDLEN and NEWLEN are their lengths. */
-
- static int
- different (const char *old, const char *new, size_t oldlen, size_t newlen)
- {
- int order;
-
- order = memcmp (old, new, min (oldlen, newlen));
-
- if (order == 0)
- return oldlen - newlen;
- return order;
- }
-
- /* Output the line in linebuffer LINE to stream STREAM
- provided that the switches say it should be output.
- If requested, print the number of times it occurred, as well;
- LINECOUNT + 1 is the number of times that the line occurred. */
-
- static void
- writeline (const struct linebuffer *line, FILE *stream, int linecount)
- {
- if (linecount == 0)
- fwrite (line->buffer, 1, line->length, stream);
- }
-
- /* Process input file INFILE with output to OUTFILE.
- If either is "-", use the standard I/O stream for it instead. */
-
- static void
- check_file (const char *infile, const char *outfile)
- {
- FILE *istream;
- FILE *ostream;
- struct linebuffer lb1, lb2;
- struct linebuffer *thisline, *prevline, *exch;
- char *prevfield, *thisfield;
- size_t prevlen, thislen;
- int match_count = 0;
-
- if (!strcmp (infile, "-"))
- istream = stdin;
- else
- istream = fopen (infile, "r");
- if (istream == NULL)
- {
- fprintf (stderr, "%s: error opening %s\n", program_name, infile);
- exit (1);
- }
-
- if (!strcmp (outfile, "-"))
- ostream = stdout;
- else
- ostream = fopen (outfile, "w");
- if (ostream == NULL)
- {
- fprintf (stderr, "%s: error opening %s\n", program_name, outfile);
- exit (1);
- }
-
- thisline = &lb1;
- prevline = &lb2;
-
- initbuffer (thisline);
- initbuffer (prevline);
-
- if (readline (prevline, istream) == 0)
- goto closefiles;
- prevfield = prevline->buffer;
- prevlen = prevline->length;
-
- while (!feof (istream))
- {
- int match;
- if (readline (thisline, istream) == 0)
- break;
- thisfield = thisline->buffer;
- thislen = thisline->length;
- match = !different (thisfield, prevfield, thislen, prevlen);
-
- if (match)
- ++match_count;
-
- if (!match)
- {
- writeline (prevline, ostream, match_count);
- exch = prevline;
- prevline = thisline;
- thisline = exch;
- prevfield = thisfield;
- prevlen = thislen;
- if (!match)
- match_count = 0;
- }
- }
-
- writeline (prevline, ostream, match_count);
-
- closefiles:
- if (ferror (istream) || fclose (istream) == EOF)
- {
- fprintf (stderr, "%s: error reading %s\n", program_name, infile);
- exit (1);
- }
-
- if (ferror (ostream) || fclose (ostream) == EOF)
- {
- fprintf (stderr, "%s: error writing %s\n", program_name, outfile);
- exit (1);
- }
-
- freebuffer (&lb1);
- freebuffer (&lb2);
- }
-
- int
- main (int argc, char **argv)
- {
- const char *infile = "-";
- const char *outfile = "-";
- int optind = 1;
-
- program_name = argv[0];
-
- if (optind < argc)
- infile = argv[optind++];
-
- if (optind < argc)
- outfile = argv[optind++];
-
- if (optind < argc)
- {
- fprintf (stderr, "%s: too many arguments\n", program_name);
- exit (1);
- }
-
- check_file (infile, outfile);
-
- exit (0);
- }
-